﻿using System;
using JetBrains.Annotations;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Interfaces.Views;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.ViewModels;

namespace MugenMvvmToolkit.Infrastructure
{
    /// <summary>
    ///     Represents the class that is used to initialize the IOC adapter for unit tests.
    /// </summary>
    public class UnitTestInitializationStrategy : IInitializationStrategy
    {
        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="UnitTestInitializationStrategy" /> class.
        /// </summary>
        public UnitTestInitializationStrategy()
        {
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="UnitTestInitializationStrategy" /> class.
        /// </summary>
        public UnitTestInitializationStrategy([NotNull]Func<IInitializationContext, bool> canInitializeViewDelegate, [NotNull] Func<IViewModel, string, IView> getViewDelegate)
        {
            Should.NotBeNull(canInitializeViewDelegate, "canInitializeViewDelegate");
            Should.NotBeNull(getViewDelegate, "getViewDelegate");
            GetViewDelegate = getViewDelegate;
            CanInitializeViewDelegate = canInitializeViewDelegate;
        }

        #endregion

        #region Properties

        /// <summary>
        ///     Gets or sets the view factory.
        /// </summary>
        public Func<IViewModel, string, IView> GetViewDelegate { get; set; }

        /// <summary>
        /// Gets or sets the delegate that is used for initialize view.
        /// </summary>
        public Func<IInitializationContext, bool> CanInitializeViewDelegate { get; set; }

        /// <summary>
        ///     Gets the current <see cref="IIocAdapter" />.
        /// </summary>
        protected IIocAdapter IocAdapter { get; private set; }

        #endregion

        #region Implementation of IInitializationStrategy

        /// <summary>
        ///     Initializes the specified <see cref="IIocAdapter" />.
        /// </summary>
        public virtual void Initialize(IIocAdapter iocAdapter)
        {
            Should.NotBeNull(iocAdapter, "iocAdapter");
            IocAdapter = iocAdapter;
            iocAdapter.BindToBindingInfo(GetThreadManager);
            GlobalSettings.DefaultThreadManager = iocAdapter.Get<IThreadManager>();

            iocAdapter.BindToBindingInfo(GetViewModelWrapperProvider);
            iocAdapter.BindToBindingInfo(GetViewManager);
            iocAdapter.BindToBindingInfo(GetViewModelProvider);
            iocAdapter.BindToBindingInfo(GetMessageBoxProvider);

            iocAdapter.BindToBindingInfo(GetSettings);
            iocAdapter.BindToBindingInfo(GetDisplayNameProvider);
            iocAdapter.BindToBindingInfo(GetEventAggregator);

#if !NOVISUALSTATE
            IocAdapter.BindToBindingInfo<IVisualStateManager>(GetVisualStateManager);
#endif
        }

        #endregion

        #region Methods

#if !NOVISUALSTATE
        /// <summary>
        /// Gets the <see cref="IVisualStateManager"/> which will be used by default.
        /// </summary>
        /// <returns>An instance of <see cref="IVisualStateManager"/>.</returns>
        protected virtual BindingInfo<IVisualStateManager> GetVisualStateManager()
        {
            return BindingInfo<IVisualStateManager>.FromInstance(new VisualStateManager(GlobalSettings.DefaultThreadManager));
        }
#endif

        /// <summary>
        ///     Gets the <see cref="IDisplayNameProvider" /> which will be used by default.
        /// </summary>
        /// <returns>An instance of <see cref="IDisplayNameProvider" />.</returns>
        protected virtual BindingInfo<IDisplayNameProvider> GetDisplayNameProvider()
        {
            return BindingInfo<IDisplayNameProvider>.FromInstance(new DisplayNameProvider());
        }

        /// <summary>
        ///     Gets the <see cref="IViewManager" /> which will be used in all view models by default.
        /// </summary>
        /// <returns>An instance of <see cref="IViewManager" />.</returns>
        protected virtual BindingInfo<IViewManager> GetViewManager()
        {
            return BindingInfo<IViewManager>.FromInstance(new ViewManager(CanInitializeViewDelegateInternal, GetViewInternal));
        }

        /// <summary>
        ///     Gets the <see cref="IViewModelProvider" /> which will be used in all view models by default.
        /// </summary>
        /// <returns>An instance of <see cref="IViewModelProvider" />.</returns>
        protected virtual BindingInfo<IViewModelProvider> GetViewModelProvider()
        {
            return BindingInfo<IViewModelProvider>.FromInstance(new ViewModelProvider(IocAdapter));
        }

        /// <summary>
        ///     Gets the <see cref="IMessageBox" /> which will be used in all view models by default.
        /// </summary>
        /// <returns>An instance of <see cref="IMessageBox" />.</returns>
        protected virtual BindingInfo<IMessageBox> GetMessageBoxProvider()
        {
            return BindingInfo<IMessageBox>.Empty;
        }

        /// <summary>
        ///     Gets the <see cref="IThreadManager" /> which will be used in all view models by default.
        /// </summary>
        /// <returns>An instance of <see cref="IThreadManager" />.</returns>
        protected virtual BindingInfo<IThreadManager> GetThreadManager()
        {
            return BindingInfo<IThreadManager>.FromInstance(new SynchronousThreadManager());
        }

        /// <summary>
        ///     Gets the <see cref="IViewModelSettings" /> which will be used in all view models by default.
        /// </summary>
        /// <returns>
        ///     An instance of <see cref="IViewModelSettings" />.
        /// </returns>
        protected virtual BindingInfo<IViewModelSettings> GetSettings()
        {
            return BindingInfo<IViewModelSettings>.FromInstance(new DefaultViewModelSettings());
        }

        /// <summary>
        ///     Gets the <see cref="IViewModelWrapperProvider" /> which will be used by default.
        /// </summary>
        /// <returns>An instance of <see cref="IViewModelWrapperProvider" />.</returns>
        protected virtual BindingInfo<IViewModelWrapperProvider> GetViewModelWrapperProvider()
        {
            return BindingInfo<IViewModelWrapperProvider>.Empty;
        }

        /// <summary>
        ///     Gets the <see cref="IEventAggregator" /> which will be used by default.
        /// </summary>
        /// <returns>An instance of <see cref="IEventAggregator" />.</returns>
        protected virtual BindingInfo<IEventAggregator> GetEventAggregator()
        {
            return BindingInfo<IEventAggregator>.FromInstance(new EventAggregator());
        }

        private IView GetViewInternal(IViewModel viewModel, string s)
        {
            var @delegate = GetViewDelegate;
            Should.MethodBeSupported(@delegate != null,
                "IViewManager.GetView(IViewModel viewModel, string viewBindingName = null)");
// ReSharper disable once PossibleNullReferenceException
            return @delegate(viewModel, s);            
        }

        private bool CanInitializeViewDelegateInternal(IInitializationContext initializationContext)
        {
            Func<IInitializationContext, bool> viewDelegate = CanInitializeViewDelegate;
            if (viewDelegate == null)
                return false;
            return viewDelegate(initializationContext);
        }

        #endregion
    }
}